#include <mono/parser/property_tree_parser.h>
#include <sstream>

namespace mono {

PropertyNodePtr PropertyTreeParser::parse(const std::string & input) {
	_tokens.clear();
	_tokens.reserve(input.size() / 10);
	
	SimpleTokenizer tokenizer;
	
	tokenizer.addToken("{", PropertyTokenBlockStart);
	tokenizer.addToken("}", PropertyTokenBlockEnd);
	tokenizer.addToken("\n", PropertyTokenEOL);
	
	tokenizer.addVerbatim(std::make_pair("\"", "\""));
	tokenizer.addComment(std::make_pair("//", "\n"));
	tokenizer.addComment(std::make_pair("#", "\n"));
	tokenizer.addComment(std::make_pair("/*", "*/"));
	
	tokenizer.addIgnore(" ");
	tokenizer.addIgnore("\t");
	tokenizer.addIgnore("\r");
	
	tokenizer.tokenize(input, _tokens);
	
	
	
	PropertyNodePtr root(new PropertyNode("root"));
	if(!root) {
		return root;
	}
	
	_braceCount = _currentToken = 0;
	bool bret = parseBlock(root);
		
	return bret ? root : PropertyNodePtr();
}


bool PropertyTreeParser::parseBlock(PropertyNodePtr & node) 
{
	std::vector<std::string> attributes;
	
	while (_currentToken < _tokens.size()) {
		const Token & token = _tokens[_currentToken];
		
		if (token.first == PropertyTokenBlockStart) {
			++_braceCount;
			PropertyNodePtr newNode(new PropertyNode(attributes.at(0)));
			if(!newNode) {
				return false;
			}
		
			for (size_t i = 1; i < attributes.size(); ++i)
				newNode->addAttribute(attributes[i]);
		
			attributes.clear();
		
			++_currentToken;
			parseBlock(newNode);
			if(!newNode) {
				return false;
			}
			node->addNode(newNode);
		} else if (token.first == PropertyTokenBlockEnd) {
			if (_braceCount-- == 0){
				return false;
			}
			
			if (!attributes.empty()) {
				addLine(attributes, node);
				attributes.clear();
			}
			
			break;
		} else if (token.first == PropertyTokenEOL) {
			if (!attributes.empty()) {
				addLine(attributes, node);
				attributes.clear();
			}
		} else {
			attributes.push_back(token.second);
		}
		
		++_currentToken;
	}
	if(_braceCount != 0) {
		return false;
	}
	return true;
}

void PropertyTreeParser::addLine(const std::vector<std::string> & line, PropertyNodePtr & node) const 
{
	if (line.size() > 1) {
		std::stringstream ss;
		for(size_t i = 1; i < line.size(); i++) {
			ss<<line[i];
		}
		node->addProperty(Property(line[0], ss.str()));
	} else if (line.size() == 1) {	// key will be assigned an id if there's none
		std::stringstream ss;
		ss << node->getNumProperties();
		node->addProperty(Property(ss.str(), line[0]));
	}
}

}